package org.example.ServletServer.Engine;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.example.ServletServer.Engine.Support.HttpHeaders;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;

//实现了HttpServletResponse
//下面这个类实现了servlet规范，本质是对httpResponse的包装
public class HttpServletResponseImplement implements HttpServletResponse {
    final HttpExchange httpExchange;
    int status = 200;
    int bufferSize = 1024;
    String contentType;
    Boolean callOutput = null;
    final HttpHeaders headers;
    Boolean committed = false;
    ServletOutputStream output;
    PrintWriter writer;
    long contentLength = 0;
    List<Cookie> cookies = null;
    public HttpServletResponseImplement(HttpExchange httpExchange) {
        this.httpExchange = httpExchange;
        this.headers = new HttpHeaders(httpExchange.getResponseHeaders());
        this.setContentType("text/html");
    }
    @Override
    public String getContentType() {
        return contentType;
    }
    //返回写入响应体的outputStream，返回前要做检查（callOutput）
    // 第一次get要初始化callOutput为true，
    // 这个标志是为了保证同时只能向outputStream和Writer其中之一写入返回体，
    // 同时操作这俩个会出现很多问题，比如二者编码不同（一个字节流，一个字符流，可能用不同方式编码），
    // 可能写入内容会顺序混乱
    // 可能会错误地关闭底层outputStream
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (callOutput == null) {
            commitHeaders(0);
            this.output = new ServletOutputStreamImpl(this.httpExchange.getResponseBody());
            this.callOutput = Boolean.TRUE;
            return this.output;
        }
        if (callOutput.booleanValue()) {
            return this.output;
        }
        throw new IllegalStateException("Cannot open output stream when writer is opened.");
    }
    @Override
    public PrintWriter getWriter() throws IOException {
        if(callOutput == null) {
            commitHeaders(0);
            this.writer = new PrintWriter(httpExchange.getResponseBody(), true, StandardCharsets.UTF_8);
            callOutput = Boolean.FALSE;
            return this.writer;
        }
        if(!callOutput.booleanValue()){
            return this.writer;
        }
        throw new IllegalStateException("Cannot open writer when output stream is opened.");
    }
    @Override
    public void setContentLength(int len) {
        this.contentLength = len;
    }
    @Override
    public void setContentLengthLong(long len) {
        this.contentLength = len;
    }
    @Override
    public void setContentType(String s) {
        setHeader("Content-Type",s);
        this.contentType = s;
    }
    //已经获取过输出流，就不能改动bufferSize
    // ，只能在获取前设置
    @Override
    public void setBufferSize(int size) {
        if (this.callOutput != null) {
            throw new IllegalStateException("Output stream or writer is opened.");
        }
        if (size < 0) {
            throw new IllegalArgumentException("Invalid size: " + size);
        }
        this.bufferSize = size;
    }
    @Override
    public int getBufferSize() {
        return this.bufferSize;
    }
    //在往输出流写完内容后可以调用此方法清空缓冲区，确保数据全部发送出去了
    @Override
    public void flushBuffer() throws IOException {
        if (this.callOutput == null) {
            throw new IllegalStateException("Output stream or writer is not opened.");
        }
        if (this.callOutput.booleanValue()) {
            this.output.flush();
        } else {
            this.writer.flush();
        }
    }
    //如果没有提交，不能resetBuffer，要flush后才能清空buffer
    @Override
    public void resetBuffer() {
        checkNotCommitted();
    }
    @Override
    public boolean isCommitted() {
        return this.committed;
    }
    //提交后恢复初始状态
    @Override
    public void reset() {
        checkNotCommitted();
        this.status = 200;
        this.headers.clearHeaders();
    }

    @Override
    public void addCookie(Cookie cookie) {
        checkNotCommitted();
        if (this.cookies == null) {
            this.cookies = new ArrayList<>();
        }
        this.cookies.add(cookie);
    }

    @Override
    public void sendError(int sc, String msg) throws IOException {
        checkNotCommitted();
        this.status = sc;
        commitHeaders(-1);
    }

    @Override
    public void sendError(int sc) throws IOException {
        sendError(sc, "Error");
    }
    //重定向原来是这么回事，响应头加一个字段Location，状态码改一下，然后不返回响应体
    @Override
    public void sendRedirect(String location) throws IOException {
        checkNotCommitted();
        this.status = 302;
        this.headers.setHeader("Location", location);
        commitHeaders(-1);
    }

    @Override
    public void setStatus(int sc) {
        checkNotCommitted();
        this.status = sc;
    }

    @Override
    public int getStatus() {
        return this.status;
    }


    //---------------------------以下是对header 操作----------------------------------------------

    @Override
    public boolean containsHeader(String name) {
        return this.headers.containsHeader(name);
    }

    @Override
    public String getHeader(String name) {
        return this.headers.getHeader(name);
    }

    @Override
    public Collection<String> getHeaders(String name) {
        List<String> hs = this.headers.getHeaders(name);
        if (hs == null) {
            return List.of();
        }
        return hs;
    }

    @Override
    public Collection<String> getHeaderNames() {
        return Collections.unmodifiableSet(this.headers.getHeaderNames());
    }

    @Override
    public void setDateHeader(String name, long date) {
        checkNotCommitted();
        this.headers.setDateHeader(name, date);
    }

    @Override
    public void addDateHeader(String name, long date) {
        checkNotCommitted();
        this.headers.addDateHeader(name, date);
    }

    @Override
    public void setHeader(String name, String value) {
        checkNotCommitted();
        this.headers.setHeader(name, value);
    }

    @Override
    public void addHeader(String name, String value) {
        checkNotCommitted();
        this.headers.addHeader(name, value);
    }

    @Override
    public void setIntHeader(String name, int value) {
        checkNotCommitted();
        this.headers.setIntHeader(name, value);
    }

    @Override
    public void addIntHeader(String name, int value) {
        checkNotCommitted();
        this.headers.addIntHeader(name, value);
    }

    //http工作原理，先返回响应头，再返回响应体，浏览器根据头的信息处理响应体，
    //也就是说，在调用getResponseBody前一定要先调用sendResponseHeaders
    //length为0代表以块的方式发送数据
    void commitHeaders(long length) throws IOException {
        this.httpExchange.sendResponseHeaders(this.status, length);
        this.committed = true;
    }

    //写完返回体后close流
    public void cleanup() throws IOException {
        if (this.callOutput != null) {
            if (this.callOutput.booleanValue()) {
                this.output.close();
            } else {
                this.writer.close();
            }
        }
    }

    void checkNotCommitted() {
        if (this.committed) {
            throw new IllegalStateException("Response is committed.");
        }
    }




    //以下未实现-----------------------------------------------

    @Override
    public String encodeURL(String s) {
        return null;
    }

    @Override
    public String encodeRedirectURL(String s) {
        return null;
    }

    @Override
    public String getCharacterEncoding() {
        return null;
    }

    @Override
    public void setCharacterEncoding(String s) {

    }


    @Override
    public void setLocale(Locale locale) {

    }

    @Override
    public Locale getLocale() {
        return null;
    }

}
